package com.decent.common.util.http;

import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Xss参数过滤工具类
 *
 * @author leiyy
 */
@Slf4j
public class XssUtil {
    /**
     * 验证code非法字符
     */
    private final static Pattern[] CODE_PATTERN = new Pattern[]{
            // Script fragments
            Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
            // src='...'
            Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // lonely script tags
            Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // eval(...)
            Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // expression(...)
            Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // javascript:...
            Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
            // vbscript:...
            Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
            // 空格英文单双引号
            Pattern.compile("[\\s\'\"]+", Pattern.CASE_INSENSITIVE),
            // onload(...)=...
            Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // alert
            Pattern.compile("alert(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("<", Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL),
            //html关键字
            Pattern.compile("(<(script|iframe|embed|frame|frameset|object|img|applet|body|html|style|layer|link|ilayer|meta|bgsound))"),
            //特殊符号
            Pattern.compile("[`~@#$^&*()+=|{}':;\"\\[\\].<>《》/?~！@#￥……&*（）——|{}【】‘；：”“。，、？ ]"),
            //sql关键字
            Pattern.compile("(?:')|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute)\\b)", Pattern.CASE_INSENSITIVE)
    };
    /**
     * 验证Url非法字符
     * https://testvegas.promo.ejiaofei.cn
     */
    private final static Pattern[] URL_PATTERN = new Pattern[]{
            //是不是我们域名开头
            Pattern.compile("^(https?://promo.ejiaofei.cn/*|https?://testvegas.ejiaofei.com/*|https?://testvegas.promo.ejiaofei.cn/*)"),
            //验证是不是url格式
            Pattern.compile("^(((ht|f)tps?):\\/\\/)?[\\w-]+(\\.[\\w-]+)+([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?$"),
            //特殊符号
            Pattern.compile("[`~@#$^*()+|{}'\"\\[\\]<>《》~！@#￥……*（）——|{}【】‘；：”“。，、？ ]")
    };

    /**
     * xss校验函数
     *
     * @param value 需要校验的字符
     * @return 返回值：true 表示存在xss漏洞，false：不存在
     */
    @SneakyThrows
    public static boolean checkUrlIsXss(String value) {
        if (!StrUtil.isNotBlank(value)) {
            return false;
        }
        value = URLDecoder.decode(value, "UTF-8");
        Matcher matcher = URL_PATTERN[0].matcher(value);
        if (!matcher.find()) {
            log.error("[{}]请求IP[{}]Url参数被xss过滤：[{}]", value, GetIpUtil.getIp(), matcher.pattern());
            return true;
        }
        matcher = URL_PATTERN[1].matcher(value);
        if (!matcher.find()) {
            log.error("[{}]请求IP[{}]Url参数被xss过滤：[{}]", value, GetIpUtil.getIp(), matcher.pattern());
            return true;
        }
        matcher = URL_PATTERN[2].matcher(value);
        if (matcher.find()) {
            log.error("[{}]请求IP[{}]Url参数被xss过滤：[{}]", value, GetIpUtil.getIp(), matcher.pattern());
            return true;
        }
        return false;
    }

    /**
     * xss校验函数
     *
     * @param value 需要校验的字符
     * @return 返回值：true 表示存在xss漏洞，false：不存在
     */
    @SneakyThrows
    public static boolean checkCodeIsXss(String value) {
        if (!StrUtil.isNotBlank(value)) {
            return false;
        }
        value = URLDecoder.decode(value, "UTF-8");
        for (Pattern scriptPattern : CODE_PATTERN) {
            Matcher matcher = scriptPattern.matcher(value);
            if (matcher.find()) {
                log.error("[{}]请求IP[{}]Code参数被xss过滤：[{}]", value, GetIpUtil.getIp(), scriptPattern.pattern());
                return true;
            }
        }
        return false;
    }
}